package com.xhj.mybatis.builder;

import com.xhj.mybatis.session.Configuration;
import com.xhj.mybatis.type.JdbcType;
import com.xhj.mybatis.type.TypeAliasRegistry;
import com.xhj.mybatis.type.TypeHandler;
import com.xhj.mybatis.type.TypeHandlerRegistry;
import org.dom4j.Element;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

/**
 * BaseBuilder
 *
 * @author XJks
 * @description 构建器基类
 */

public class BaseBuilder {

    /**
     * 待构建的配置对象
     */
    protected final Configuration configuration;

    /**
     * 类型别名注册器，用于将字符串别名映射为 Java 类型（类）
     */
    protected final TypeAliasRegistry typeAliasRegistry;

    /**
     * 类型处理器注册器，用于管理和查找类型处理器（TypeHandler），实现 Java 类型和数据库类型的转换
     */
    protected final TypeHandlerRegistry typeHandlerRegistry;

    public BaseBuilder(Configuration configuration) {
        this.configuration = configuration;
        this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
        this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    /**
     * 将字符串 value 转换为 Boolean 类型，如果 value 为 null，则返回默认值 defaultValue
     *
     * @param value        字符串表示的布尔值
     * @param defaultValue 默认值
     * @return 转换后的 Boolean 值
     */
    protected Boolean booleanValueOf(String value, Boolean defaultValue) {
        // 判断 value 是否为 null，如果是，则返回 defaultValue，否则将 value 转为 Boolean 类型
        return value == null ? defaultValue : Boolean.valueOf(value);
    }

    /**
     * 将字符串 value 转换为 Integer 类型，如果 value 为 null，则返回默认值 defaultValue
     *
     * @param value        字符串表示的整数值
     * @param defaultValue 默认值
     * @return 转换后的 Integer 值
     */
    protected Integer integerValueOf(String value, Integer defaultValue) {
        // 判断 value 是否为 null，如果是，则返回 defaultValue，否则将 value 转为 Integer 类型
        return value == null ? defaultValue : Integer.valueOf(value);
    }

    /**
     * 将以逗号分隔的字符串 value 转换为字符串集合 Set<String>，如果 value 为 null，则使用默认值 defaultValue
     *
     * @param value        以逗号分隔的字符串
     * @param defaultValue 默认值
     * @return 转换后的字符串集合
     */
    protected Set<String> stringSetValueOf(String value, String defaultValue) {
        // 如果 value 为 null，则使用 defaultValue，否则使用 value 本身
        value = value == null ? defaultValue : value;
        // 使用逗号分割字符串，得到字符串数组，然后将其转换为 HashSet 集合
        return new HashSet<>(Arrays.asList(value.split(",")));
    }

    /**
     * 根据类型别名字符串解析并返回对应的Class对象。
     *
     * @param alias 类型别名字符串，例如"string"、"int"等
     * @param <T>   泛型类型参数
     * @return 对应的Class对象
     */
    protected <T> Class<? extends T> resolveAlias(String alias) {
        return typeAliasRegistry.resolveAlias(alias);
    }

    /**
     * 根据类型别名字符串解析并返回对应的Class对象。
     * 例如，传入"string"会返回java.lang.String.class。
     * 如果传入的别名为null，则返回null。
     * 如果解析过程中出现异常，则抛出自定义的BuilderException异常。
     *
     * @param alias 类型别名字符串
     * @param <T>   泛型类型参数
     * @return 对应的Class对象
     */
    protected <T> Class<? extends T> resolveClass(String alias) {
        // 如果别名为null，直接返回null
        if (alias == null) {
            return null;
        }
        try {
            // 调用resolveAlias方法，根据别名获取Class对象
            return resolveAlias(alias);
        } catch (Exception e) {
            // 如果解析过程中出现异常则抛出
            throw new RuntimeException("Error resolving class. Cause: " + e, e);
        }
    }

    /**
     * 根据字符串别名解析为对应的 JdbcType 枚举类型。
     * 如果传入的别名为 null，直接返回 null，表示没有指定类型。
     * 如果别名无法匹配任何 JdbcType 枚举值，会抛出异常，附带详细错误信息。
     *
     * @param alias 类型别名字符串
     * @return 对应的 JdbcType 枚举类型
     */
    protected JdbcType resolveJdbcType(String alias) {
        // 如果传入的别名为 null，直接返回 null，表示没有指定类型
        if (alias == null) {
            return null;
        }
        try {
            // 尝试将字符串别名转换为 JdbcType 枚举对象
            // JdbcType.valueOf(alias) 会根据别名查找对应的枚举常量
            return JdbcType.valueOf(alias);
        } catch (IllegalArgumentException e) {
            // 如果别名无法匹配任何 JdbcType 枚举值，会抛出异常
            throw new RuntimeException("Error resolving JdbcType. Cause: " + e, e);
        }
    }

    /**
     * 根据给定的 Java 类型和 TypeHandler 类型，解析并返回对应的 TypeHandler 实例。
     * 如果 typeHandlerType 为 null，则返回 null。
     * 如果 typeHandlerRegistry 中已存在该 TypeHandler，则直接返回。
     * 否则会创建一个新的 TypeHandler 实例并返回。
     *
     * @param javaType        Java 类型（可为 null，部分场景下不需要）
     * @param typeHandlerType TypeHandler 的 Class 类型
     * @return TypeHandler 实例，或 null
     */
    protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
        // 如果 typeHandlerType 为 null，说明没有指定类型处理器，直接返回 null
        if (typeHandlerType == null) {
            return null;
        }
        // 从类型处理器注册表中查找是否已经有该类型的 TypeHandler 实例
        // 这里 javaType 参数被忽略，详见 issue #746
        TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
        // 如果注册表中没有找到对应的 TypeHandler，则创建一个新的实例
        if (handler == null) {
            handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
        }
        // 返回找到或新建的 TypeHandler 实例
        return handler;
    }

    /**
     * 获取所有子节点，并将每个子节点的 name 属性作为 key，value 属性作为 value，组装成 Properties 返回。
     *
     * @param context 当前 XML 节点
     * @return Properties 对象，包含子节点的 name-value 键值对
     */
    public Properties getChildrenAsProperties(Element context) {
        // 创建一个新的 Properties 对象，用于存储结果
        Properties properties = new Properties();
        // 遍历当前节点的所有子节点
        for (Element child : context.elements()) {
            // 获取子节点的 name 属性
            String name = child.attributeValue("name");
            // 获取子节点的 value 属性
            String value = child.attributeValue("value");
            // 如果 name 和 value 都不为 null，则将其作为一组键值对放入 properties
            if (name != null && value != null) {
                properties.setProperty(name, value);
            }
        }
        // 返回组装好的 Properties 对象
        return properties;
    }

    /**
     * 自定义工具方法:
     * 1.从 Dom4j 的 Element 元素中，依次尝试根据多个可能的属性名获取字符串属性值。
     * 2.如果某个属性名对应的值不为空，则立即返回该值。
     * 3.如果所有属性名都没有对应的值，则返回 null。
     *
     * @param element Dom4j 的 Element 元素
     * @param names   属性名列表
     * @return 字符串属性值
     */
    protected String getStringAttributeOfDom4jElement(Element element, String... names) {
        for (String name : names) {
            String value = element.attributeValue(name);
            if (value != null && !value.isEmpty()) {
                return value;
            }
        }
        return null;
    }

    /**
     * 自定义工具方法:
     * 1.从 Dom4j 的 Element 元素中，依次尝试根据多个可能的属性名获取布尔类型属性值。
     * 2.如果某个属性名对应的值不为空，则将该值转换为 Boolean 并返回。
     * 3.如果所有属性名都没有对应的值，则返回 null。
     *
     * @param element Dom4j 的 Element 元素
     * @param names   属性名列表
     * @return 布尔类型属性值
     */
    protected Boolean getBooleanAttributeOfDom4jElement(Element element, String... names) {
        for (String name : names) {
            String value = element.attributeValue(name);
            if (value != null && !value.isEmpty()) {
                return Boolean.valueOf(value);
            }
        }
        return null;
    }

    /**
     * 自定义工具方法:
     * 1.从 Dom4j 的 Element 元素中，获取指定名称的布尔类型属性值。
     * 2.如果属性不存在或值为 null，则返回默认值 def。
     * 3.否则，将属性值转换为 Boolean 类型并返回。
     *
     * @param name 属性名称
     * @param def  默认值
     * @return 布尔类型属性值
     */
    public Boolean getBooleanAttributeOfDom4jElement(Element element, String name, Boolean def) {
        // 从 attributeValue 中获取指定名称的属性值（字符串类型）
        String value = element.attributeValue(name);
        // 如果属性值为 null，则返回默认值 def；否则将字符串转换为 Boolean 类型后返回
        return value == null ? def : Boolean.valueOf(value);
    }

    /**
     * 自定义工具方法:
     * 1.从 Dom4j 的 Element 元素中获取指定名称的长整型属性值。
     * 2.如果属性不存在或值为 null，则返回 null。
     * 3.否则，将属性值转换为 Long 类型并返回。
     *
     * @param element Dom4j 的 Element 元素
     * @return 长整型属性值
     */
    public Long getLongAttributeOfDom4jElement(Element element, String name) {
        String value = element.attributeValue(name);
        return value == null ? null : Long.valueOf(value);
    }

    /**
     * 自定义工具方法:
     * 1.从 Dom4j 的 Element 元素中获取指定名称的整型属性值。
     * 2.如果属性不存在或值为 null，则返回 null。
     * 3.否则，将属性值转换为 Integer 类型并返回。
     *
     * @param element Dom4j 的 Element 元素
     * @return 整型属性值
     */
    public Integer getIntegerAttributeOfDom4jElement(Element element, String name) {
        String value = element.attributeValue(name);
        return value == null ? null : Integer.valueOf(value);
    }
}
